ServerOptions: different input & output device names only works on osx
[supercollider.git] / examples / GUI examples / GUI_examples2.scd
blobe0dcbfd19bd8cdc1644dfd7d2c9fa76c9e5f6a7f
1 GUI.cocoa;              // use Mac OS X native GUI
2 GUI.swing;              // use Java GUI
4 // Creating a window.
6 w = Window.new("a control panel", Rect(20, 400, 440, 360));
7 w.front; // make window visible and front window.
10 // Views are controlled by setting properties.
12 w.view.background = Color.rand;
13 w.view.background = Color.rand;
14 w.view.background = Color.rand;
15 w.view.background = Gradient(Color.blue,Color.green,\v);
16 w.view.background = Gradient(Color.black,Color.red,\h);
17 w.view.background = Gradient(Color.black,Color.red,\h, 16);
18 w.view.background = Gradient(Color.black,Color.red,\h, 128);
19 w.view.background = HiliteGradient(Color.blue,Color.yellow,\v);
20 w.view.background = HiliteGradient(Color.blue,Color.yellow,\v, 16);
21 w.view.background = HiliteGradient(Color.blue,Color.yellow,\v, 256);
22 w.view.background = HiliteGradient(Color.red(0.6),Color.green,\h, 16);
23 w.view.background = HiliteGradient(Color.red(0.6),Color.green,\h, 256);
26 Routine({
27         30.do {
28                 w.view.background = 
29                         HiliteGradient(Color.rand(0.0,1.0),Color.rand(0.0,1.0),
30                                         [\h,\v].choose, 100, rrand(0.1,0.9));
31                 0.5.wait;
32         };
33 }).play(AppClock);
37 // The FlowLayout decorator places views one after another.
39 w.view.decorator = FlowLayout(w.view.bounds);
41 // A button that adds another one like itself.
44 f = {
45         // the arguments for creating a view are the window it is in,
46         // and the bounds of the view
47         b = Button(w, 75 @ 24);
48         // states defines the colors and label for the button in different states.
49         b.states = [["Add", Color.black, Color.rand]];
50         b.action = f;
52 f.value;
55 // A button that changes the background.
58 b = Button(w, 75 @ 24);
59 b.states = [["Backgnd", Color.white, Color.rand]];
60 b.action = {
61                 w.view.background = Color.rand(0.0,1.0); // fallback for SwingOSC
62                 w.view.background = 
63                         HiliteGradient(Color.rand(0.0,1.0),Color.rand(0.0,1.0),
64                                         [\h,\v].choose, 100, rrand(0.1,0.9));
68 // A multi-state button.
71 b = Button(w, 75 @ 24);
72 b.states = [
73         ["Red", Color.white, Color.red],
74         ["Green", Color.black, Color.green],
75         ["Blue", Color.white, Color.blue],
76         ["Yellow", Color.black, Color.yellow]
78 b.action = {| view |
79         if (view.value == 0) { w.view.background = Color.yellow };
80         if (view.value == 1) { w.view.background = Color.red };
81         if (view.value == 2) { w.view.background = Color.green };
82         if (view.value == 3) { w.view.background = Color.blue };
86 // A slider that controls window transparency.
88 // works on Mac OS X only
90 w.view.decorator.nextLine;
91 v = Slider(w, 130 @ 24);
92 v.action = {| view | 
93         // Sliders output values from zero to one.
94         w.alpha = view.value; 
96 v.value = 1;
99 w.alpha = 1;
100 w.front;
102 // A slider that controls window width.
105 w.view.decorator.nextLine;
106 v = Slider(w, 130 @ 24);
107 v.action = {| view | 
108         var bounds;
109         bounds = w.bounds;
110         bounds.width = 400 + (400 * view.value);
111         w.bounds = bounds;
115 w.close;
116 w = nil;
118 // A more useful window.
121 // start server
122 s.boot;
126 // define a synth
127 SynthDef("window-test", { arg note = 36, fc = 1000, rq = 0.25, bal=0, amp=0.4, gate = 1;
128                 var x;
129                 x = Mix.fill(4, { 
130                         LFSaw.ar((note + {0.1.rand2}.dup).midicps, 0, 0.02) 
131                 });
132                 x = RLPF.ar(x, fc, rq).softclip;
133                 x = RLPF.ar(x, fc, rq, amp).softclip;
134                 x = Balance2.ar(x[0], x[1], bal);
135                 x = x * EnvGen.kr(Env.cutoff, gate, doneAction: 2);
136                 Out.ar(0, x);
137         }, [0.1, 0.1, 0.1, 0.1, 0.1, 0]
138 ).add;
142 // make the window
143 w = Window("another control panel", Rect(20, 400, 440, 360));
144 w.front; // make window visible and front window.
145 w.view.decorator = FlowLayout(w.view.bounds);
146 w.view.background = Color.rand(0.0,1.0); // fallback for SwingOSC
147 w.view.background = HiliteGradient(Color.rand(0.0,1.0),Color.rand(0.0,1.0),
148                                         [\h,\v].choose, 100, rrand(0.1,0.9));
152 // add a button to start and stop the sound.
153 b = Button(w, 75 @ 24);
154 b.states = [["Start", Color.black, Color.green],["Stop", Color.white, Color.red]];
155 b.action = {|view|
156                 if (view.value == 1) {
157                         s.sendMsg("/s_new", "window-test", 9999, 0, 0);
158                 };
159                 if (view.value == 0) {
160                         s.sendMsg("/n_set", 9999, "gate", 0);
161                 };
166 // add a label
167 w.view.decorator.nextLine;
168 v = StaticText(w, 80 @ 24);
169 v.string = "Note";
170 v.stringColor = Color.white;
171 v.align = \right;
174 v.align = \left;
175 v.align = \center;
176 v.align = \right;
179 // create a ControlSpec for mapping values to correct range.
180 ~noteSpec = ControlSpec(24, 60, \lin, 1);
181 // create slider and number views.
182 ~noteSlider = Slider(w, 200 @ 24);
183 ~noteNumBox = NumberBox(w, 64 @ 24);
185 ~noteSlider.step = 1/(60-24);
186 ~noteSlider.action = {|view|
187         var note;
188         note = ~noteSpec.map(view.value);
189         ~noteNumBox.value = note;
190         s.sendMsg("/n_set", 9999, "note", note);
193 ~noteNumBox.action = {|view|
194         var note;
195         note = view.value;
196         s.sendMsg("/n_set", 9999, "note", note);
197         ~noteSlider.value = ~noteSpec.unmap(note);
199 ~noteNumBox.align = \center;
207 That is a lot of work for something simple, so I made a wrapper class. 
208 EZSlider takes care of the details for you.
211 EZSlider is a wrapper for a SCStaticText, an SCSlider, and an SCNumberBox along with the logic to manage them.
215 // create controls.
216 w.view.decorator.nextLine;
217 v = EZSlider(w, 400 @ 24, "Note", ControlSpec(24, 60, \lin, 1), 
218         {|ez| s.sendMsg("/n_set", 9999, "note", ez.value); });
219         
220 w.view.decorator.nextLine;
221 v = EZSlider(w, 400 @ 24, "Cutoff", ControlSpec(200, 5000, \exp), 
222         {|ez| s.sendMsg("/n_set", 9999, "fc", ez.value); });
223         
224 w.view.decorator.nextLine;
225 v = EZSlider(w, 400 @ 24, "Resonance", ControlSpec(0.1, 0.7), 
226         {|ez| s.sendMsg("/n_set", 9999, "rq", ez.value); });
227         
228 w.view.decorator.nextLine;
229 v = EZSlider(w, 400 @ 24, "Balance", \bipolar, 
230         {|ez| s.sendMsg("/n_set", 9999, "bal", ez.value); });
231         
232 w.view.decorator.nextLine;
233 v = EZSlider(w, 400 @ 24, "Amp", \db, 
234         {|ez| s.sendMsg("/n_set", 9999, "amp", ez.value.dbamp); });
239 There are still some problems:
240         ¥ Restarting the sound doesn't remember the slider settings.
241         ¥ cmd-period doesn't change the button.
242         ¥ Closing window doesn't stop the sound.
243 Need a more comprehensive approach.
248 var w, startButton, noteControl, cutoffControl, resonControl;
249 var balanceControl, ampControl;
250 var id, cmdPeriodFunc;
252 id = s.nextNodeID; // generate a note id.
254 // make the window
255 w = Window("another control panel", Rect(20, 400, 440, 180));
256 w.front; // make window visible and front window.
257 w.view.decorator = FlowLayout(w.view.bounds);
259 w.view.background = HiliteGradient(Color.rand(0.0,1.0),Color.rand(0.0,1.0),
260                                         [\h,\v].choose, 100, rrand(0.1,0.9));
262 // add a button to start and stop the sound.
263 startButton = Button(w, 75 @ 24);
264 startButton.states = [
265         ["Start", Color.black, Color.green],
266         ["Stop", Color.white, Color.red]
268 startButton.action = {|view|
269                 if (view.value == 1) {
270                         // start sound
271                         s.sendMsg("/s_new", "window-test", id, 0, 0, 
272                                 "note", noteControl.value,
273                                 "fc", cutoffControl.value,
274                                 "rq", resonControl.value,
275                                 "bal", balanceControl.value,
276                                 "amp", ampControl.value.dbamp);
277                 };
278                 if (view.value == 0) {
279                         // set gate to zero to cause envelope to release
280                         s.sendMsg("/n_set", id, "gate", 0);
281                 };
284 // create controls for all parameters
285 w.view.decorator.nextLine;
286 noteControl = EZSlider(w, 400 @ 24, "Note", ControlSpec(24, 60, \lin, 1), 
287         {|ez| s.sendMsg("/n_set", id, "note", ez.value); }, 36);
288         
289 w.view.decorator.nextLine;
290 cutoffControl = EZSlider(w, 400 @ 24, "Cutoff", ControlSpec(200, 5000, \exp), 
291         {|ez| s.sendMsg("/n_set", id, "fc", ez.value); }, 1000);
292         
293 w.view.decorator.nextLine;
294 resonControl = EZSlider(w, 400 @ 24, "Resonance", ControlSpec(0.1, 0.7), 
295         {|ez| s.sendMsg("/n_set", id, "rq", ez.value); }, 0.2);
296         
297 w.view.decorator.nextLine;
298 balanceControl = EZSlider(w, 400 @ 24, "Balance", \bipolar, 
299         {|ez| s.sendMsg("/n_set", id, "bal", ez.value); }, 0);
300         
301 w.view.decorator.nextLine;
302 ampControl = EZSlider(w, 400 @ 24, "Amp", \db, 
303         {|ez| s.sendMsg("/n_set", id, "amp", ez.value.dbamp); }, -6);
304         
306 // set start button to zero upon a cmd-period
307 cmdPeriodFunc = { startButton.value = 0; };
308 CmdPeriod.add(cmdPeriodFunc);
310 // stop the sound when window closes and remove cmdPeriodFunc.
311 w.onClose = {
312         s.sendMsg("/n_free", id);
313         CmdPeriod.remove(cmdPeriodFunc);